#include <assert.h>
#include <iostream>
#include <time.h>

#include "night/array.hpp"
#include "night/tensor.hpp"

using namespace night;
using namespace night::array;

int main () 
{
	const int n = 3;
	const int m = 4;
	int x[n][m] = { {1,2,3,4}, {2,3,4,5}, {3,4,5,6} };
	int y[m][n];
	int z[n][n] = { {1,2,3}, {4,5,6}, {7,8,9} };
	int t[n][n];

	array_view<int,n,m> a (x);
	array_view<int,m,n> b (y);
	array_view<int,n,n> c (z);
	array_view<int,n,n> d (t);

	b[_i][_j] = a[_j][_i];
	for( int i = 0; i<m; ++i )
		for( int j = 0; j<n; ++j ) assert ( y[i][j] == x[j][i] );

	b[_i][_j] = a[_j][_i];
	b[1][_j] = a[_j][2];
	for( int i = 0; i<m; ++i )
		for( int j = 0; j<n; ++j ) assert ( y[i][j] == ( i==1 ? x[j][2] : x[j][i] ) );

	b[_i][_j] = a[_j][_i];
	b[_i][_j] = a[1][2];
	for( int i = 0; i<m; ++i )
		for( int j = 0; j<n; ++j ) assert ( y[i][j] == x[1][2] );

	b[_i][_j] = a[_j][_i];
	b[_i][_j] = 0;
	for( int i = 0; i<m; ++i )
		for( int j = 0; j<n; ++j ) assert ( y[i][j] == 0 );

	b[_i][_j] = a[_j][_i];
	b[_i][_j] = c[_i][_i];
	int s = 0;
	for( int i = 0; i<n; ++i ) s += z[i][i];
	for( int i = 0; i<m; ++i )
		for( int j = 0; j<n; ++j ) assert ( y[i][j] == s );

	b[_i][_j] = a[_j][_i];
	c[_i][_j] = - a[_i][_k] * b[_k][_l] * a[_l][_m] * b[_m][_j] + 3 * a[_i][_k] * 2 * b[_k][_j];
	for( int i=0; i<n; ++i ) for( int j=0; j<n; ++j ) {
		int s1 = 0;
		int s2 = 0;
		for( int k=0; k<m; ++k ) {
			s2 += x[i][k] * y[k][j];
			for( int l=0; l<n; ++l ) for( int o=0; o<m; ++o ) 
				s1 += x[i][k] * y[k][l] * x[l][o] * y[o][j];
		}
		t[i][j] = -s1 + 6 * s2;
	}
	assert( c[_i][_j] == d[_i][_j] );

	assert( 3 * c[_i][_k] * c[_k][_j] == 3 * eval( c[_i][_k] * c[_k][_j] ) );

	time_t start = time (0);


	for ( int u=0; u<50000000; ++u ) {
//		c[_i][_j] = d[_i][_k] * c[_k][_j];
/*
		for ( int i=0; i<n; ++i ) for ( int j=0; j<n; ++j ) {
			int s = 0;
			for ( int k=0; k<n; ++k ) s += t[i][k] * z[k][j];
			z[i][j] = 3 * s;
		}
*/
	}

	time_t end = time (0);

	std::cout << a << std::endl;
	std::cout << b << std::endl;
	std::cout << c << std::endl;
	std::cout << d << std::endl;
	std::cout << difftime (end,start);

}
